import gobject
import gstcomponents
import recordsrc
import gst
import os

from util import config
from fvutil import fvcon

class MixerInterface(gobject.GObject):
    __gsignals__ = {
                    
            # create signals that will be emitted from mixer 
            
            # emitted when the message finished playing back
            'end_message' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_INT,)),
            # user_idle is when no incoming packets from network (no packets for 5 seconds)
            'user_idle' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_PYOBJECT,)),
            # user_active is when packets start to come in from network (new packets for 5 secs)
            'user_active' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_PYOBJECT,)), 
            'ack_push_to_talk' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_INT,)),
                    
            'started_record' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_INT,)),
            'stopped_record' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_INT,)),
            'started_play' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_INT,)),
            'stopped_play' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_INT,)),
            'my_ssrc' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_UINT,)),

    }

    def __init__( self, live_audio_dest):
        """
            @param live_audio_dest: the broadcast address that the live audio needs to be sent to
            @type live_audio_dest: String
        """
        gobject.GObject.__init__(self)
        LIVE_AUDIO_PORT = config.get_int('fv', 'live-audio-port')
        self._file_sender = gstcomponents.FileInputSender(LIVE_AUDIO_PORT)
        self._file_sender.connect('eos', self._fileFinished)
        self._rtp_receiver = gstcomponents.RtpStreamsReceiver(LIVE_AUDIO_PORT, 
                enableLevelMeasurement=True,
                enableTimeShifting=True,
                play=True)
        self._rtp_receiver.connect('new-rtp-stream', self._newRTPStream)
        self._rtp_receiver.connect('rtp-stream-timeout', self._timeoutRTPStream)
        
        #: map ssrc to pipelines
        self._mixers = {}
        self._file_players = {}
        #: map id to file finished True/False
        self._file_finished = {}
        #: map guid to their type and properties
        self._inputs = {}    
        #: maps guid to (volume, pan)                                        
        self._mixer_params = {}    
        #: time machine units    
        self._tmus = {}
        
        self._my_ssrc = 0
        print 'Live audio port:',LIVE_AUDIO_PORT,';live audio_dest',live_audio_dest
        self._mic_controller = recordsrc.MicController(live_audio_dest, 
                                                        LIVE_AUDIO_PORT)
        self._mic_controller.connect( 'my_ssrc', self._startTalk )
        
        self._path = config.get('async.folders', fvcon.AUDIO)
        if not os.path.exists(self._path):
            print "MixerInterface: Creating path for audio"
            os.makedirs(self._path)
        self.temp_rec_file_name = self._path + "temp_audio.wav"
        # user_id -> tivo_id
        self.__tivos_awaiting_ssrcs = {}

    def _startTalk(self, obj, ssrc):
        print "My SSRC:",ssrc
        # silence my volume
        self._my_ssrc = ssrc
        self._mixer_params[self._my_ssrc] = (0,0)
        self.emit('my_ssrc', ssrc)
        
    def _newRTPStream(self, obj, session, ssrc, pt, id):
        """
            Callback when new RTP stream appears in the mixer
            @param obj: L{RtpStreamsReceiver}
        """
        print "New RTP stream:",session,ssrc,pt,id
        
        self._mixers[ssrc] = self._rtp_receiver.getMixer( id )
        volume, pan = self._mixer_params.get(ssrc, (1,0) )
        self._mixers[ssrc].setVolume(volume)
        self._mixers[ssrc].setPan(pan)

    def _timeoutRTPStream( self, obj, session, ssrc, pt, id):
        print "Timeout RTP stream:",session,ssrc,pt,id
        if ssrc in self._mixers:
            self._mixers[ssrc].remove()
        self._mixers.pop(ssrc, None)
        self._mixer_params.pop(ssrc, None)

    def _fileFinished(self, obj, id):
        self._file_finished[id] = True
        print "Finished playing",id
        
    def onAddInput(self, obj, properties ):
        """
            Add an input to the audio mixer.  
            The input can be an audio message or network stream.
            @param guid:
            @type guid: Number
            @param properties: type; IP address, port for network and filename for message
            @type properties: Dictionary
        """
        print 'properties.get(status)',properties['status']
        print properties['status']==fvcon.OFFLINE
        if properties.get('status', fvcon.OFFLINE) == fvcon.OFFLINE:
            return
        print 'wasnt offline'
        guid = properties['id']
        print properties
        print "Adding INPUT", guid
        self._inputs[guid] = properties
        
        #if guid in self.__tivos_awaiting_ssrcs:
        #    tivo = self.__tivos_awaiting_ssrcs[guid].addTee
        
    def onChange(self, obj, ui_data):
        """
            When state changes from either background or muted or inactive
            @param ui_data: id, volume, pan
                volume 0: mute
                volume 1: soft
                volume 2: loud
                volume 3: conference (TODO: need to start tivo)
            @type ui_data: Dictionary
        """
        print 'on_change',ui_data
        guid = ui_data['id']
        volume = ui_data['volume']
        pan = ui_data['pan']
        type = ui_data['type']
        
        if pan > 1:
            pan = 1
        elif pan < -1:
            pan = -1

        # if object type is modifiable
        if ui_data['type'] not in [fvcon.FV, fvcon.SERVICE, fvcon.PHONE, fvcon.AUDIO, fvcon.TIVO]:
            print "MIXER: Ignored",ui_data['type']
            return
        
        # if it was moved out of the playback area, stop it
        if volume < 0:
            if guid in self._file_players:
                player = self._file_players[guid]
                ssrc = player.getSsrc()
                self._file_finished.pop(player.getID())
                player.stop()
                player.remove()
                self._file_players.pop(guid)
                self._mixers[ssrc].remove()
                self._mixers.pop(ssrc)
                self._mixer_params.pop(ssrc)
            return
        
        
        # if the node is offline, then ignore
        properties = self._inputs.get(guid, None)
        print self._inputs
        print 'guid:',guid,properties
        if properties is None or properties.get('status', fvcon.OFFLINE) == fvcon.OFFLINE:
            print 'Offline. Ignoring.'
            return
        
        # get the ssrc
        ssrc = properties.get('ssrc', 0)
        print 'Got ssrc',ssrc
        print self._mixers
        if ssrc in self._mixers:
            # Existing
            print "MIXER: Changing VOLUME"
            self._mixers[ssrc].setVolume( volume )
            self._mixers[ssrc].setPan( pan )
            # check if file finished playing back and replay
            if guid in self._file_players and volume > 0:
                if self._file_finished[self._file_players[guid].getID()]:
                    self._file_players[guid].play()
                    self._file_finished[self._file_players[guid].getID()] = False
                    print "Replaying"
        else:
            # New
            print "New or Inactive Input",guid
            # create new pipeline for this guid
            if ui_data['type'] is fvcon.AUDIO:
                print "MIXER: Playing",properties['local_path']
                ssrc = self._startMessage( guid, properties['local_path'], volume, pan )
            else:
                print "No input streams available for ssrc:",ssrc
                return
        
        self._mixer_params[ssrc] = (volume, pan)
            
    def _startMessage(self, guid, filename, volume, pan):
        """
            Starts the file stream 
            @param guid: the source globally unique identifier
            @type guid: Number
            @param filename: name of the file to play
            @type filename: String
        """
        self._file_players[guid] = self._file_sender.addFileInput( filename )
        playerid = self._file_players[guid].getID()
        self._file_finished[playerid] = False
        ssrc = self._file_players[guid].getSsrc()
        self._inputs[guid]['ssrc'] = ssrc
        return ssrc
    
    def onOffline( self, obj, params ):
        """
            Deactivate the pipeline when an object is moved out of the audio play layer 
            or goes offline

            @param params: various parameters of user/service that has gone offline
            @type params: Dictionary
        """
        guid = params['id']
        if guid in self._inputs:
            ssrc = self._inputs[guid].get('ssrc', None)
            if ssrc and ssrc in self._mixers:
                # if it's an active pipeline stop and return True
                self._mixers[ssrc].remove()


    def onMIC(self, obj, action):
        """
            Called when push to talk is deactivated or when push to talk was inactive
            and message recording stops
            Stops recording from MIC
            @param action: 1 when push to talk on, 2 when attention pressed, 0 when push to talk off
            @type action: Number
        """
        if action in [1,2]:
            print "MIC starting"
            self._mic_controller.micUnmute()
        else:
            print "MIC stopping"
            self._mic_controller.micMute()
        self.emit( 'ack_push_to_talk', action)
            
    def onStartRecord(self, obj, recdata):
        """
            Called when the UI sends a request to start recording.
            @param recdata: dictionary with fields 
                1. 'id': temporary id
                2. 'filename': used for recording to specific file
        """
        print "OnStartRecord",recdata
        id = recdata['id']
        print "id:",id
        filename = recdata.get('filename', None)
        if filename is None:
            self._mic_controller.startRecord( self.temp_rec_file_name )
        else:
            self._mic_controller.startRecord( filename )
        print "Exit StartRecord"
        self.emit('started_record', id)
    
    def onStopRecord(self, obj, recdata):
        """
            Called when the UI sends a request to stop recording.
            @param recdata: dictionary with fields
                1. 'id': temporary id
                2. 'filename': file name to record to
        """
        
        print "OnStopRecord"
        id = recdata['id']
        self._mic_controller.stopRecord()
        filename = recdata.get('filename', None)        
        if filename is None:
            detail = {'subcommand':fvcon.ADD, 'id':id, 'type':fvcon.AUDIO, 'label':'Temp Record Audio', 
                        'local_path':self.temp_rec_file_name, 'status':-1, 
                        'priority':fvcon.PRIORITY_LOW }
        else:
            detail = {'subcommand':fvcon.ADD, 'id':id, 'type':fvcon.AUDIO, 'label':'Temp Record Audio', 
                        'local_path':filename, 'status':-1, 
                        'priority':fvcon.PRIORITY_LOW }
 
        # add to the mixer so you can playback for review
        self.onAddInput( None, detail )
        self.emit('stopped_record', id)
        
    def onStartPlay(self, obj, playdata):
        """
            Called when the UI sends a request to start playing a message
        """
        print "OnStartPlay",playdata
        print "id:", playdata['id']
        id = playdata['id']
        
        playinfo = {'id':id, 'volume':1, 'pan':0, 'type':fvcon.AUDIO }
        self.onChange( None, playinfo )
        self.emit('started_play', id)
        
    def onStopPlay(self, obj, playdata):
        """
            Called when the UI sends a request to stop playing a message
        """
        print 'OnStopPlay'
        print "id", playdata['id']
        id = playdata['id']
        playinfo = {'id':id, 'volume':0, 'pan':0, 'type':fvcon.AUDIO }
        self.onChange( None, playinfo )
        self.emit('stopped_play', id)
        
    def onStartTivo(self, obj, user_ids, tivo_id, filename):
        """
            Record inputs of the people in ids to the filename
            @param user_ids: The ids of the users to record
            @param tivo_id: The ID of the tivo object (generated externally)
            @param filename: The filename to record to
        """
        mixers = []
        # Map to the SSRC
        for user_id in user_ids:
            ssrc = self._inputs[user_id].get('ssrc', None)
            if ssrc:
                print 'GOT ssrc %s for TIVO' %(ssrc,)
                print self._mixers
                mixer_id = self._mixers[ssrc].id
                mixers.append(mixer_id)
            else:
                self.__tivos_awaiting_ssrcs[user_id] = tivo_id
        mixers.append(self._mixers[self._my_ssrc].id)
        print 'About to tivo with mixers %s to filename %s' % (mixers, filename)
        tmu = self._rtp_receiver.createTivo(mixers, filename)
        self._tmus[tivo_id] = tmu
    
    def onTivoAddUser(self, obj, id):
        """
            TODO: 
                Find an existing tmu and add this new id
            @param id: the mixer id
        """
        pass
        
    def onStopTivo(self, obj, tivo_id):
        """
            Stop a tivo session from recording.
            
            @param tivo_id: The id of the tmu to stop recording.
        """
        self._tmus[tivo_id].stop()
        del self._tmus[tivo_id]


gobject.type_register( MixerInterface )
